<script setup lang="ts">
import {Group, Mesh, Object3D} from "three";
import {useEmitt} from "@/hooks/web/useEmitt";
import {useSeek} from "@tresjs/core";
import {ShallowRef} from "vue";
import {TreeInstance} from "element-plus";

const {seek} = useSeek()

// 内容 Group
const contentGroup = inject<Group>('contentGroup')!

interface StructTree {
  label: string
  uuid: string
  children?: StructTree[]
  object3dId: string
  // isGroup: boolean
  isMesh: boolean
  hasEvent: boolean
}

// 当前编辑对象
const editObject = inject<ShallowRef<Object3D | null>>('editObject')!
// 当前编辑模式
const editMode = inject<Ref<'translate' | 'rotate' | 'scale'>>('editMode')!

/** 结构树 */
const structTree = ref<StructTree[]>()
// 重新加载结构树
const changeStructTree = () => {
  const traverse = (obj: Object3D): StructTree => {
    const object3d = seek(contentGroup, 'uuid', obj.uuid)!
    const node: StructTree = {
      label: obj.name || '',
      uuid: obj.uuid,
      object3dId: object3d.uuid,
      // isGroup: (object3d as Group).isGroup,
      isMesh: (object3d as Mesh).isMesh,
      hasEvent: object3d.userData.eventList?.length > 0,
    }

    // 递归处理所有子对象（包括深层嵌套）
    if (obj.children?.length > 0) {
      node.children = obj.children.map(child => traverse(child))
    }

    return node
  }

  // 仅处理contentGroup的直接子级（children数组），但每个子级会递归包含其所有后代
  structTree.value = contentGroup.children.map(child => traverse(child))
}

// 点击节点
const treeRef = ref<TreeInstance>()
const clickNode = (data: StructTree) => {
  const uuid = data.uuid
  const object = seek(contentGroup, 'uuid', uuid)
  if(!editObject.value) {
    editMode.value = 'translate'
  }
  editObject.value = object
}

// 点击模型，改变当前结构树所选中的节点
const clickModel = (uuid: string) => {
  treeRef.value?.setCurrentKey(uuid)
}

// 拖拽节点结束
interface TreeNode {
  childNodes: TreeNode[]
  data: StructTree
  id: number
  parent: TreeNode
}
const handleNodeDrop = (
  node: TreeNode,
  endNode: TreeNode,
  position: 'before' | 'after' | 'inner' | 'none'
) => {
  if (position === 'none') return

  const objectNode = seek(contentGroup, 'uuid', node.data.uuid)!
  let endObjectNode: Object3D
  if (position === 'inner') {
    endObjectNode = seek(contentGroup, 'uuid', endNode.data.uuid)!
  } else {
    endObjectNode = seek(contentGroup, 'uuid', endNode.parent.data.uuid)!
  }

  objectNode.parent?.remove(objectNode)
  endObjectNode.add(objectNode)
}

// 是否允许成为拖拽的目标位置
const isAllowDrop = (node: TreeNode) => {
  const uuid = node.data.object3dId

  return !(seek(contentGroup, 'uuid', uuid)! as Mesh).isMesh
}

const init = () => {
  useEmitt({name: 'changeStructTree', callback: changeStructTree})
  useEmitt({name: 'clickModel', callback: clickModel})
}
init()

</script>

<template>
  <el-tree
    ref="treeRef"
    :data="structTree"
    empty-text="当前暂无模型"
    node-key="uuid"
    draggable
    :allow-drop="isAllowDrop"
    :expand-on-click-node="false"
    @node-click="clickNode"
    @node-drop="handleNodeDrop"
  >
    <template #default="{ data }: {data: StructTree}">
      <el-space>
        <el-tag v-if="data.isMesh" type="primary">Mesh</el-tag>
        <el-tag v-else type="primary">Group</el-tag>
        <el-tag v-if="data.hasEvent" type="warning">Event</el-tag>
        <el-tooltip effect="dark" :content="data.label" :show-after="1000">
          <div :class="data.uuid === editObject?.uuid ? ['text-blue'] : []">
            {{ data.label }}
          </div>
        </el-tooltip>
      </el-space>
    </template>
  </el-tree>
</template>

<style scoped lang="scss">

</style>
